#include <stdio.h>
#include <getch.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

//定义发送数据缓冲区
#define BUF_SIZE (4096)
//地址类型重定义
typedef struct sockaddr* SAP;
//棋盘15*15
char board[15][15] = {}; 
//当前正在落子角色
char piece = '@';
//落子位置
int cnt = 0,x =7,y=7;

void drop();
void show_board();
int check();

int main(int argc,const char* argv[])
{
	//网络地址初始化
	int sock = socket(AF_INET,SOCK_DGRAM,0);
	if(0 > sock)
	{
		perror("socket");
		return EXIT_FAILURE;
	}

	struct sockaddr_in dest_addr = {};
	dest_addr.sin_family = AF_INET;
	dest_addr.sin_port = htons(10077);
	dest_addr.sin_addr.s_addr = inet_addr("172.20.10.3");//172.20.10.3 47.110.244.211
	socklen_t addrlen = sizeof(dest_addr);

	char buf[BUF_SIZE] = {};
	
	//初始化棋盘
	for(int i = 0; i < 15; i++)
		for(int j = 0;j < 15;j++)
			board[i][j] = '+';
	
	//链接服务端
	sprintf(buf,"客户端%d链接",sock);
	int ret_size = sendto(sock,buf,strlen(buf)+1,0,(SAP)&dest_addr,addrlen);
	if(0 >= ret_size)
	{
		perror("sendto");
		return EXIT_FAILURE;
	}
	
	//接收服务端信息，判断是先手/后手
	ret_size = recvfrom(sock,buf,BUF_SIZE,0,(SAP)&dest_addr,&addrlen);
	printf("%s\n",buf);
	char flag = buf[0];
	
	//等待游戏开始
	ret_size = recvfrom(sock,buf,BUF_SIZE,0,(SAP)&dest_addr,&addrlen);
	printf("%s\n",buf);
	usleep(3000000);
	
	//如果是先手，则先手落子
	if(flag == 'F')
	{
		piece = '#';
		//显示棋盘
		show_board();
		//落子
		drop();
		//发送落子地点
		sprintf(buf,"%d %d",x,y);
		ret_size = sendto(sock,buf,strlen(buf)+1,0,(SAP)&dest_addr,addrlen);
		if(0 >= ret_size || 0 == strcmp(buf,"quit"))
		{
			perror("sendto");
			return EXIT_FAILURE;
		}
	}
	          
	for(;;)
    {   
		show_board();
        //接收
		ret_size = recvfrom(sock,buf,BUF_SIZE,0,(SAP)&dest_addr,&addrlen);
		if(0 >= ret_size)
		{
			perror("recvfrom");
			return EXIT_FAILURE;
		}
		sscanf(buf,"%d %d",&x,&y);
		
		//切换成对方棋子
		piece = (piece=='@')?'#':'@';
		board[x][y] = piece; 
		
		//判断是否游戏结束
		if(check(1,0) || check(0,1) || check(1,1) || check(1,-1))
		{   
            show_board();
            printf("对方胜利\n");
            return 0;
        } 
        
        //切换回己方棋子
		piece = (piece=='@')?'#':'@';
		show_board();
		
        //落子
		drop();
       
        //发送
		sprintf(buf,"%d %d",x,y);
		int ret_size = sendto(sock,buf,strlen(buf)+1,0,(SAP)&dest_addr,addrlen);
		if(0 >= ret_size)
		{
			perror("sendto");
			return EXIT_FAILURE;
		} 
		
        //判断是否游戏结束
		if(check(1,0) || check(0,1) || check(1,1) || check(1,-1))
		{   
            show_board();
            printf("%c棋手胜利",piece);
            return 0;
        }   
    }   
}
//检查落子位置是否五子连通(判断上下左右以及斜向)
int check(int x1,int y1) 
{
    cnt = 0;
    int m_x = x1,m_y = y1; 
    //先判断一个方向(例如输入(1,0)，下方向)
    for(int i = 0; i < 4; i++)
    {
        if(board[x+m_x][y+m_y] == piece)
        {
            cnt++;
            m_x += x1;
            m_y += y1;
        }
        else
            break;
    }
    m_x = x1;m_y =y1;
    //再判断一个方向(输入的是(1,0)，上方向)
    for(int j = 0; j < 4; j++)
    {
        if(board[x-m_x][y-m_y] == piece)
        {
            cnt++;
            m_x += x1;
            m_y += y1;
        }
        else
            break;
    }
    //如果一条线上有4个子以上相同相同，则返回1，否则返回0
    return cnt/4;
}
//落子函数
void drop()
{
    for(;;)
    {
    	//显示当前光标的位置
        printf("\33[%d;%dH",x+1,(y+1)*2);
        //获取键盘输入
        switch(getch())
        {
        	//183-186为上下左右，10为回车
            case 183:x>0  && x--;break;
            case 184:x<14 && x++;break;
            case 185:y<14 && y++;break;
            case 186:y>0  && y--;break;
            case 10:
            	//当光标位置没有子时，可以落子
                if(board[x][y] == '+')
                {
                    board[x][y] = piece;
                    return;
                }
                break;
        }
    }

}
//显示棋盘函数
void show_board()
{
        system("clear");
        for(int i=0;i<15;i++)
        {
            for(int j = 0;j<15;j++)
            {
                printf(" %c",board[i][j]);
            }
            printf("\n");
        }

}

